﻿using System;

using DigitalQuran.Extensions;

namespace DigitalQuran
{
    public struct Address
    {
        /// <summary>
        /// Gets Chapter Number Component of Address
        /// </summary>
        public int Chapter { get; private set; }

        /// <summary>
        /// Gets Verse Number Component of Address
        /// </summary>
        public int Verse { get; private set; }

        /// <summary>
        /// Gets Word Number Component of Address (if any)
        /// </summary>
        public int? Word { get; internal set; }

        /// <summary>
        /// Gets Word Part Number Component of Address (if any)
        /// </summary>
        public int? WordPart { get; private set; }

        /// <summary>
        /// Returns String Representation of Address
        /// </summary>
        /// <returns>String Representation of Address</returns>
        public override string ToString()
        {
            if (Word.HasValue)
                if (WordPart.HasValue)
                    return string.Format("{0}:{1}:{2}:{3}", Chapter, Verse, Word.Value, WordPart.Value);
                else
                    return string.Format("{0}:{1}:{2}", Chapter, Verse, Word.Value);
            else
                return string.Format("{0}:{1}", Chapter, Verse);
        }

        /// <summary>
        /// Returns String Representation of Address
        /// </summary>
        /// <param name="stringFormat">A standard or custom numeric format string</param>
        /// <returns>String Representation of Address</returns>
        public string ToString(string stringFormat)
        {
            if (Word.HasValue)
                if (WordPart.HasValue)
                    return string.Format("{0}:{1}:{2}:{3}", Chapter.ToString(stringFormat), Verse.ToString(stringFormat), Word.Value.ToString(stringFormat), WordPart.Value.ToString(stringFormat));
                else
                    return string.Format("{0}:{1}:{2}", Chapter.ToString(stringFormat), Verse.ToString(stringFormat), Word.Value.ToString(stringFormat));
            else
                return string.Format("{0}:{1}", Chapter.ToString(stringFormat), Verse.ToString(stringFormat));
        }

        /// <summary>
        /// Converts Address into string having Arabic Numerials
        /// </summary>
        /// <returns>Arabic String Representation of Address</returns>
        public string ToArabicString()
        {
            if (Word.HasValue)
                if (WordPart.HasValue)
                    return string.Format("{0}:{1}:{2}:{3}", Chapter.ToArabic(), Verse.ToArabic(), Word.Value.ToArabic(), WordPart.Value.ToArabic());
                else
                    return string.Format("{0}:{1}:{2}", Chapter.ToArabic(), Verse.ToArabic(), Word.Value.ToArabic());
            else
                return string.Format("{0}:{1}", Chapter.ToArabic(), Verse.ToArabic());
        }

        /// <summary>
        /// Determines whether the specified object instances are considered equal.
        /// </summary>
        /// <param name="obj">Other Object</param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (!(obj is Address))
                return false;

            return this == (Address)obj;
        }

        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            return Chapter.GetHashCode() + Verse.GetHashCode() + Word.GetHashCode() + WordPart.GetHashCode();
        }

        /// <summary>
        /// Returns whether the specified two address instances are considered equal.
        /// </summary>
        /// <param name="a">Address object to compare</param>
        /// <param name="b">Address object to compare with</param>
        /// <returns>Returns true if equal</returns>
        public static bool operator ==(Address a, Address b)
        {
            return (a.Chapter == b.Chapter) &&
                   (a.Verse == b.Verse) &&
                   (a.Word == b.Word) &&
                   (a.WordPart == b.WordPart);
        }

        /// <summary>
        /// Returns whether the specified two address instances are considered inequal.
        /// </summary>
        /// <param name="a">Address object to compare</param>
        /// <param name="b">Address object to compare with</param>
        /// <returns>Return false if equal</returns>
        public static bool operator !=(Address a, Address b)
        {
            return (a.Chapter != b.Chapter) ||
                   (a.Verse != b.Verse) ||
                   (a.Word != b.Word) ||
                   (a.WordPart != b.WordPart);
        }

        /// <summary>
        /// Create new Instance of Address from specific chapter and its verse
        /// </summary>
        /// <param name="chapter">Chapter number</param>
        /// <param name="verse">Verse number of chapter</param>
        /// <returns>Returns new instance of <see cref="DigitalQuran.Address"/></returns>
        public static Address Create(int chapter, int verse)
        {
            return new Address()
            {
                Verse = verse,
                Chapter = chapter,
            };
        }

        /// <summary>
        /// Create new Instance of Address from specific chapter and its verse and verse word number
        /// </summary>
        /// <param name="chapter">Chapter number</param>
        /// <param name="verse">Verse number of chapter</param>
        /// <param name="word">Word number of specified verse</param>        
        /// <returns>Returns new instance of <see cref="DigitalQuran.Address"/></returns>
        public static Address Create(int chapter, int verse, int word)
        {
            return new Address()
            {
                Verse = verse,
                Chapter = chapter,
                Word = word,
            };
        }

        /// <summary>
        /// Create new Instance of Address from specific chapter and its verse and verse word number
        /// </summary>
        /// <param name="chapter">Chapter number</param>
        /// <param name="verse">Verse number of chapter</param>
        /// <param name="word">Word number of specified verse</param>        
        /// <param name="part">Word Part number of specified word</param>
        /// <returns>Returns new instance of <see cref="DigitalQuran.Address"/></returns>
        public static Address Create(int chapter, int verse, int word, int part)
        {
            return new Address()
            {
                Verse = verse,
                Chapter = chapter,
                Word = word,
                WordPart = part,
            };
        }

        /// <summary>
        /// Converts the string representation of a Address to its equivalent Address        
        /// </summary>
        /// <param name="address">A string containing a address to convert.</param>
        /// <exception cref="System.ArgumentNullException">address is null</exception>
        /// <exception cref="System.ArgumentException">Invalid address</exception>
        /// <exception cref="System.FormatException">address part is not in the correct format</exception>
        /// <exception cref="System.OverflowException">any part of address represents a number less than System.Int32.MinValue or greater than System.Int32.MaxValue.</exception>
        /// <returns>Returns new Instance of Address</returns>
        public static Address Parse(string address)
        {
            if (address == null)
                throw new ArgumentNullException("address must not be null");

            //address = address.TrimEnd(')').TrimStart('(');
            var addressParts = address.Split(':');

            switch (addressParts.Length)
            {
                case 2:
                    return Create(int.Parse(addressParts[0]), int.Parse(addressParts[1]));
                case 3:
                    return Create(int.Parse(addressParts[0]), int.Parse(addressParts[1]), int.Parse(addressParts[2]));
                case 4:
                    return Create(int.Parse(addressParts[0]), int.Parse(addressParts[1]), int.Parse(addressParts[2]), int.Parse(addressParts[3]));
                default:
                    throw new ArgumentException("Invalid Address");
            }
        }
    }
}